﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using aoprinter;
using aoprinter.Model;
using aoprinter.Draw;

namespace aoprinter.Core
{
    static class PageOper
    {
        static public void MakePageSequence(this ModReport report)
        {
            for (int i = 0; i < report.Pages.Count; i++)
            {
                report.Pages[i].PageIndex = i + 1;
                report.Pages[i].Report = report;
            }
        }

        static public void InheritLocation(this ModReport report)
        {
            if (report.Head != null)
            {
                if (report.Head.Location.X == ModReport.ZeroPoint.X)
                {
                    report.Head.Location = new PointF(0, report.Head.Location.Y);
                }
                if (report.Head.Location.Y == ModReport.ZeroPoint.Y)
                {
                    report.Head.Location = new PointF(report.Head.Location.X, 0);
                }
                report.Head.Blocks.ForEach(p => p.InheritLocation(report.Position));
            }
            if (report.Foot != null)
            {
                if (report.Foot.Location.X == ModReport.ZeroPoint.X)
                {
                    report.Foot.Location = new PointF(0, report.Foot.Location.Y);
                }
                if (report.Foot.Location.Y == ModReport.ZeroPoint.Y)
                {
                    report.Foot.Location = new PointF(report.Foot.Location.X, report.Size.Height - report.Foot.Size.Height);
                }
                report.Foot.Blocks.ForEach(p => p.InheritLocation(report.Position));
            }
            for (int i = 0; i < report.Pages.Count; i++)
            {
                if (report.Pages[i].Position == EnmPosition.Suspend)
                {
                    report.Pages[i].Position = report.Position;
                }
                if (report.Pages[i].Location.X == ModReport.ZeroPoint.X)
                {
                    report.Pages[i].Location = new PointF(0, report.Pages[i].Location.Y);
                }
                if (report.Pages[i].Location.Y == ModReport.ZeroPoint.Y)
                {
                    report.Pages[i].Location = new PointF(report.Pages[i].Location.X, 0);
                }
                report.Pages[i].Blocks.ForEach(p => p.InheritLocation(report.Pages[i].Position));
            }
        }

        static public bool HasPageHead(this ModPage page)
        {
            if (!page.NoPagHeader && page.Report.Head != null)
            {
                return true;
            }
            return false;
        }

        static public bool HasPageFoot(this ModPage page)
        {
            if (!page.NoPageFooter && page.Report.Foot != null)
            {
                return true;
            }
            return false;
        }

        static public void AutoSizeLocation(this ModReport report, Drawer drawer)
        {
            if (report.Head != null)
            {
                report.Head.AutoSizeLocation(report.Size, drawer);
            }
            if (report.Foot != null)
            {
                report.Foot.AutoSizeLocation(report.Size, drawer);
            }
            report.Pages.ForEach(p => p.AutoSizeLocation(drawer));
        }

        static public void AutoSizeLocation(this ModPage page, Drawer drawer)
        {
            if (page.Size.Width == ModReport.AutoSize.Width)
            {
                page.Size = new SizeF(page.Report.Size.Width, page.Size.Height);
            }
            if (page.Size.Height == ModReport.AutoSize.Height)
            {
                float pageHeight = page.Report.Size.Height;
                if (page.HasPageHead())
                {
                    pageHeight -= page.Report.Head.Size.Height;
                }
                if (page.HasPageFoot())
                {
                    pageHeight -= page.Report.Foot.Size.Height;
                }
                page.Size = new SizeF(page.Size.Width, pageHeight);
            }
            page.Blocks.ForEach
                ((p) =>
                {
                    p.AutoSizeLocation(page.Size, drawer);
                });
            if (page.Position == EnmPosition.Relative)
            {
                RelativePosition(page.Blocks, page.Size, drawer);
            }
        }

        static public void AutoSizeLocation(this ModPageHeader head, SizeF size, Drawer drawer)
        {
            if (head.Size.Width == ModPageHeader.AutoSize.Width)
            {
                head.Size = new SizeF(size.Width, head.Size.Height);
            }
            head.Blocks.ForEach(p => p.AutoSizeLocation(head.Size, drawer));
            if (head.Position == EnmPosition.Relative)
            {
                RelativePosition(head.Blocks, head.Size, drawer);
            }
        }

        static public void AutoSizeLocation(this ModPageFooter foot, SizeF size, Drawer drawer)
        {
            if (foot.Size.Width == ModPageFooter.AutoSize.Width)
            {
                foot.Size = new SizeF(size.Width, foot.Size.Height);
            }
            foot.Blocks.ForEach(p => p.AutoSizeLocation(foot.Size, drawer));
            if (foot.Position == EnmPosition.Relative)
            {
                RelativePosition(foot.Blocks, foot.Size, drawer);
            }
        }

        static private SizeF RelativePosition(List<ModBlock> blocks, SizeF size, Drawer drawer)
        {
            if (blocks == null)
            {
                return SizeF.Empty;
            }
            BlockRegion blockRegion = new BlockRegion(size);
            blocks.ForEach(
                (p) =>
                {
                    if (p.BlockType == EnmBlockType.Delimiter)
                    {
                        p.Location = blockRegion.GetFitPosition(p.Location,
                            drawer.GetLineSize(p.Location, PointF.Add(p.Location, p.Size), (p as ModDelimiter).LineStyle),
                            p.Interval);
                    }
                    else
                    {
                        p.Location = blockRegion.GetFitPosition(p.Location, p.Size, p.Interval);
                    }
                });
            return blockRegion.ParentSize;
        }

        static public void Print(this ModReport report, Drawer drawer)
        {
            if (drawer == null)
            {
                return;
            }
            report.MakePageSequence();
            report.InheritLocation();
            report.AutoSizeLocation(drawer);
            int totalPageNumber = 0;
            for (int i = 0; i < report.Pages.Count; i++)
            {
                drawer.StartPage(report.Pages[i].PageIndex, report.Pages[i].Size);
                report.Pages[i].Print(drawer);
                drawer.EndPage(report.Pages[i].HasPageHead() ? report.Head.Size.Height : 0,
                    report.Pages[i].HasPageFoot() ? report.Foot.Size.Height : 0);
                totalPageNumber += report.Pages[i].NoPageNum ? 0 : drawer.ImageNumber(report.Pages[i].PageIndex);
            }
            report.PageTotal = totalPageNumber;
            if (report.Head != null && report.Head.PageNum != null)
            {
                report.Head.PageNum.PageTotal = totalPageNumber;
            }
            if (report.Foot != null && report.Foot.PageNum != null)
            {
                report.Foot.PageNum.PageTotal = totalPageNumber;
            }
            int nowPageIndex = 0;
            for (int i = 0; i < report.Pages.Count; i++)
            {
                for (int j = 0; j < drawer.ImageNumber(report.Pages[i].PageIndex); j++)
                {
                    if (!report.Pages[i].NoPageNum)
                    {
                        nowPageIndex++;
                    }
                    if (report.Pages[i].HasPageHead() || report.Pages[i].HasPageFoot())
                    {
                        drawer.ChangeImge(report.Pages[i].PageIndex, j);
                        if (report.Pages[i].HasPageHead())
                        {
                            report.Head.Print(drawer, report.Pages[i].NoPageNum, nowPageIndex);
                        }
                        if (report.Pages[i].HasPageFoot())
                        {
                            report.Foot.Print(drawer, report.Pages[i].NoPageNum, nowPageIndex);
                        }
                        drawer.SaveImage();
                    }
                }
            }
        }

        static public void Print(this ModPage page, Drawer drawer)
        {
            drawer.FillWhiteBackground(new RectangleF(page.Location, page.Size));
            for (int i = 0; i < page.Blocks.Count; i++)
            {
                page.Blocks[i].Print(drawer, page.Location);
            }
        }

        static public void Print(this ModPageHeader head, Drawer drawer, bool noPageNum, int nowPageIndex)
        {
            drawer.FillWhiteBackground(new RectangleF(head.Location, head.Size));
            for (int i = 0; i < head.Blocks.Count; i++)
            {
                head.Blocks[i].Print(drawer, head.Location);
            }
            if (!noPageNum && head.PageNum != null)
            {
                head.PageNum.PageNow = nowPageIndex;
                head.PageNum.Print(drawer, head.Location);
            }
        }

        static public void Print(this ModPageFooter foot, Drawer drawer, bool noPageNum, int nowPageIndex)
        {
            drawer.FillWhiteBackground(new RectangleF(foot.Location, foot.Size));
            for (int i = 0; i < foot.Blocks.Count; i++)
            {
                foot.Blocks[i].Print(drawer, foot.Location);
            }
            if (!noPageNum && foot.PageNum != null)
            {
                foot.PageNum.PageNow = nowPageIndex;
                foot.PageNum.Print(drawer, foot.Location);
            }
        }

        static private void Print(this ModPageNum pageNum, Drawer drawer, PointF location)
        {
            if (pageNum != null)
            {
                drawer.DrawText(pageNum.GetNumberText(),
                    new PointF(pageNum.Location.X + location.X, pageNum.Location.Y + location.Y),
                    pageNum.TextStyle);
            }
        }

        static public string GetNumberText(this ModPageNum pageNum)
        {
            return pageNum.PageFormat.Replace(ModPageNum.PAGT_TOTAL, pageNum.PageTotal.ToString())
                .Replace(ModPageNum.PAGE_NUM, pageNum.PageNow.ToString());
        }
    }
}
